﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Marmat.Forms.Skin
{

    /// <summary>
    /// 管理相关的选项卡页集。
    /// </summary>
    [ToolboxItem(true)]
    [Designer("Marmat.Forms.Skin.Design.TabControlDesigner")]
    public partial class TabControl : System.Windows.Forms.UserControl, ISupportInitialize
    {
        private Dictionary<TabPage, Rectangle> dic = new Dictionary<TabPage, Rectangle>();
        private TabPage mouse_tabPage;
        private TabPage designMode_SelectedTab;

        /// <summary>
        /// 初始化 Marmat.Forms.Skin.TabControl 类的新实例。
        /// </summary>
        public TabControl()
        {
            SetStyle(ControlStyles.ResizeRedraw, true); // 尺寸变化后绘制
            SetStyle(ControlStyles.Opaque, false); // 改善绘图质量
            SetStyle(ControlStyles.SupportsTransparentBackColor, true); // 使控件支持透明色
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);

            this.Font = new Font("微软雅黑", 9.0F);
            this.ShowTabs = true;
            base.BackColor = this.m_BackColor;
            this.TabPages = new TabPageCollection(this);
        }

        /// <summary>
        /// 引发 System.Windows.Forms.Control.Paint 事件。
        /// </summary>
        /// <param name="e">包含事件数据的 System.Windows.Forms.PaintEventArgs。</param>
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            dic.Clear();
            int left = 0;
            int top = 0;
            Rectangle bounds = default;
            TabPage selectedTab = this.SelectedTab;
            if (this.designMode_SelectedTab is not null)
                selectedTab = this.designMode_SelectedTab;
            Pen pen = new Pen(Color.FromArgb(238, 238, 238));
            if (selectedTab != null)
                pen.Color = this.SelectedTab.BorderColor;
            ImageList imageList = this.ImageList;
            int width = this.Width - base.Padding.Left - base.Padding.Right;
            int height = this.Height - base.Padding.Top - base.Padding.Bottom;
            switch (this.alignment)
            {
                case TabAlignment.Top:
                    e.Graphics.DrawLine(pen, base.Padding.Left, base.Padding.Top - 1, this.Width - base.Padding.Right, base.Padding.Top - 1);
                    left += this.Offset;
                    bounds = new Rectangle(0, 0, left, this.ItemSize.Height);
                    break;
                case TabAlignment.Left:
                    e.Graphics.DrawLine(pen, base.Padding.Left - 1, base.Padding.Top - 1, base.Padding.Left - 1, this.Height - base.Padding.Bottom);
                    top += this.Offset;
                    bounds = new Rectangle(0, 0, this.ItemSize.Width, top);
                    break;
                case TabAlignment.Bottom:
                    top = this.Height - base.Padding.Bottom + 1;
                    e.Graphics.DrawLine(pen, base.Padding.Left, this.Height - base.Padding.Bottom, this.Width - left, this.Height - base.Padding.Bottom);
                    left += this.Offset;
                    bounds = new Rectangle(0, top, left, this.ItemSize.Height);
                    break;
                case TabAlignment.Right:
                    left = this.Width - base.Padding.Right + 1;
                    e.Graphics.DrawLine(pen, this.Width - base.Padding.Right, base.Padding.Top - 1, this.Width - base.Padding.Right, this.Height - base.Padding.Bottom);
                    top += this.Offset;
                    bounds = new Rectangle(left, 0, this.ItemSize.Width, top);
                    break;
            }
            if (bounds.Width > 0 && bounds.Height > 0)
            {
            }


            int index = 0;
            Image image;
            TabPage page;
            DrawItemState itemState;
            foreach (TabPage tabPage in this.TabPages)
            {
                page = tabPage;
                if (!tabPage.TabVisible && !this.DesignMode)
                {
                    index++;
                    continue;
                }
                image = tabPage.Image;
                if (image is null && imageList != null)
                {
                    if ((tabPage.ImageIndex >= 0))
                        image = imageList.Images[tabPage.ImageIndex];
                    else if (!string.IsNullOrEmpty(tabPage.ImageKey))
                        image = imageList.Images[tabPage.ImageKey];
                    if (object.Equals(this.SelectedTab, tabPage) || object.Equals(this.mouse_tabPage, tabPage))
                    {
                        if ((tabPage.FocusImageIndex >= 0))
                            image = imageList.Images[tabPage.FocusImageIndex];
                        else if (!string.IsNullOrEmpty(tabPage.FocusImageKey))
                            image = imageList.Images[tabPage.FocusImageKey];
                    }
                    index++;
                }

                Size size = e.Graphics.MeasureString(tabPage.Text, this.Font).ToSize();
                if (this.alignment == TabAlignment.Top || this.alignment == TabAlignment.Bottom)
                    if (image is not null)
                        bounds = new Rectangle(left, top, Math.Max(size.Width + image.Width + 6, this.ItemSize.Width), this.ItemSize.Height);
                    else
                        bounds = new Rectangle(left, top, Math.Max(size.Width, this.ItemSize.Width), this.ItemSize.Height);
                else if (this.alignment == TabAlignment.Left || this.alignment == TabAlignment.Right)
                    if (image is not null)
                        bounds = new Rectangle(left, top, this.ItemSize.Width, Math.Max(size.Width + image.Height + 6, this.ItemSize.Height));
                    else
                        bounds = new Rectangle(left, top, this.ItemSize.Width, Math.Max(size.Width, this.ItemSize.Height));
                else
                    continue;
                dic[tabPage] = bounds;
                itemState = DrawItemState.None;
                if (selectedTab == tabPage)
                    itemState |= DrawItemState.Selected;
                if (this.mouse_tabPage == tabPage)
                    itemState |= DrawItemState.HotLight;
                if (!tabPage.Enabled)
                    itemState |= DrawItemState.Disabled;
                else
                    itemState |= DrawItemState.Default;
                if (this.DrawItem is not null)
                    this.DrawItem.Invoke(this, new DrawItemEventArgs(e.Graphics, this.Font, bounds, index, itemState));
                else
                    drawItem(new PaintEventArgs(e.Graphics, bounds));
                if (this.alignment == TabAlignment.Top || this.alignment == TabAlignment.Bottom)
                    left += bounds.Width + this.Interval;
                else if (this.alignment == TabAlignment.Left || this.alignment == TabAlignment.Right)
                    top += bounds.Height + this.Interval;
            }

            void drawItem(PaintEventArgs e)
            {
                StringFormat format = new();
                format.Alignment = StringAlignment.Center;
                format.LineAlignment = StringAlignment.Center;
                // 绘制焦点线条
                var pen = new Pen(Color.Gray, 2);
                if (itemState.HasFlag(DrawItemState.Selected) || itemState.HasFlag(DrawItemState.HotLight))
                {
                    pen.Color = Color.FromArgb(52, 143, 228);
                    switch (this.Alignment)
                    {
                        case TabAlignment.Top:
                            e.Graphics.DrawLine(pen, e.ClipRectangle.X, e.ClipRectangle.Bottom - 1, e.ClipRectangle.Right, e.ClipRectangle.Bottom - 1);
                            break;
                        case TabAlignment.Bottom:
                            e.Graphics.DrawLine(pen, e.ClipRectangle.X, e.ClipRectangle.Y, e.ClipRectangle.Right, e.ClipRectangle.Y);
                            break;
                        case TabAlignment.Left:
                            e.Graphics.DrawLine(pen, e.ClipRectangle.Right - 1, e.ClipRectangle.Y, e.ClipRectangle.Right - 1, e.ClipRectangle.Bottom);
                            break;
                        case TabAlignment.Right:
                            e.Graphics.DrawLine(pen, e.ClipRectangle.X, e.ClipRectangle.Y, e.ClipRectangle.X, e.ClipRectangle.Bottom);
                            break;
                    }
                    pen.Color = Color.FromArgb(52, 143, 228);
                }
                Brush brush = new SolidBrush(pen.Color);

                Rectangle imgLocation = default;
                Rectangle textRectangle = e.ClipRectangle;
                if (image is not null && string.IsNullOrEmpty(page.Text))
                    imgLocation = new Rectangle(e.ClipRectangle.X + (e.ClipRectangle.Width - image.Width) / 2, e.ClipRectangle.Y + (e.ClipRectangle.Height - image.Height) / 2, image.Width, image.Height);
                switch (this.Alignment)
                {
                    case TabAlignment.Top:
                    case TabAlignment.Bottom:
                        if (image is not null && !string.IsNullOrEmpty(page.Text))
                        {
                            imgLocation = new Rectangle(e.ClipRectangle.X, e.ClipRectangle.Y + (e.ClipRectangle.Height - image.Height) / 2, image.Width, image.Height);
                            textRectangle = new Rectangle(e.ClipRectangle.X + imgLocation.Width + 3, e.ClipRectangle.Y, e.ClipRectangle.Width - (imgLocation.Width + 3), e.ClipRectangle.Height);
                        }
                        break;
                    case TabAlignment.Left:
                    case TabAlignment.Right:
                        format.FormatFlags = StringFormatFlags.DirectionVertical;
                        if (image is not null && !string.IsNullOrEmpty(page.Text))
                        {
                            imgLocation = new Rectangle(e.ClipRectangle.X + (e.ClipRectangle.Width - image.Width) / 2, e.ClipRectangle.Y + 3, image.Width, image.Height);
                            textRectangle = new Rectangle(e.ClipRectangle.X, e.ClipRectangle.Y + imgLocation.Height + 3, e.ClipRectangle.Width, e.ClipRectangle.Height - (imgLocation.Height + 3));
                        }
                        break;
                }
                if (imgLocation != default)
                    e.Graphics.DrawImage(image, imgLocation);
                if (!string.IsNullOrEmpty(page.Text))
                    e.Graphics.DrawString(page.Text, this.Font, brush, textRectangle, format);
                brush.Dispose();
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            TabPage tabPage = this.TabPages.FirstOrDefault(item => item.TabVisible && this.GetTabPageBounds(item).Contains(e.Location));
            Cursor cursor = tabPage != null ? Cursors.Hand : Cursors.Default;
            if (!this.Cursor.Equals(cursor))
                this.Cursor = cursor;
            if (!object.Equals(this.mouse_tabPage, tabPage))
            {
                this.mouse_tabPage = tabPage;
                this.Invalidate();
            }
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            this.Cursor = Cursors.Default;
            if (this.mouse_tabPage != null)
            {
                this.mouse_tabPage = null;
                this.Invalidate();
            }
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                int @int = 0;
                var tabPages = this.TabPages.ToArray();
                if (!this.DesignMode)
                    tabPages = tabPages.Where(item => item.TabVisible).ToArray();
                foreach (TabPage tabPage in tabPages)
                {
                    if (this.GetTabPageBounds(tabPage).Contains(e.Location))
                    {
                        ControlAccessibleObject controlAccessibleObject = (ControlAccessibleObject)base.AccessibilityObject; ;
                        if (controlAccessibleObject is not null)
                            controlAccessibleObject.NotifyClients(AccessibleEvents.Focus, @int);
                        if (!this.DesignMode)
                            this.SelectedTab = tabPage;
                        else
                        {
                            this.designMode_SelectedTab = tabPage;
                            tabPage.BringToFront();
                            tabPage.Visible = true;
                            this.Invalidate();
                        }
                        // Me.Select()
                        continue;
                    }
                    @int += 1;
                }
            }
            base.OnMouseClick(e);
        }

        public Rectangle GetTabPageBounds(TabPage tabPage)
        {
            if (dic.ContainsKey(tabPage))
                return dic[tabPage];
            else
                return default;
        }

        public void BeginInit()
        {
        }

        public void EndInit()
        {
            if (this.TabPages.Count > 0 && this.SelectedTab == null)
                this.SelectedTab = this.TabPages[0];
        }

        private void TabControl_ControlRemoved(object sender, ControlEventArgs e)
        {
            if (e.Control is TabPage)
                this.TabPages.Remove((TabPage)e.Control);
        }
    }
}
